#include <debug.h>
#include <isr.h>
#include <vga.h>
#include <mm.h>
#include <sched.h>
#include <i386.h>
#include <device.h>
#include <fs.h>
#include <serial.h>
#include <con.h>
#include <string.h>

const char* exp_name[] = {
	"Divide Error",
	"Debug Interrupt",
	"NMI Interrupt",
	"Breakpoint",
	"Interrupt on overflow",
	"BOUND range exceeded",
	"Invalid Opcode",
	"Device not available",
	"Double fault",
	"Coprocessor Segment Overrun",
	"Invalid TSS",
	"Segment not present",
	"Stack exception",
	"General protection fault",
	"Page fault",
	"Reserved",
	"Floating point exception",
	"Alignment check"
};

#define COLOR VGA_BC_RED | VGA_FC_WHITE | VGA_FC_LIGHT
#define REGS_COL 0
#define REGS_ROW 0

#define BTRC_COL 0
#define BTRC_ROW 15

//puerto serie para debugear
chardev *serialdev = NULL; 
chardev *condev = NULL;
int debug_row = 0;


//LOG EN CONSOLA
void debug_log(char* format, ...) {

	uint_32 *param;
	char *pchar;
	char buffer[32]; 

	if (condev == NULL) {
		condev = con_open();
		strcpy(buffer, "########## SYSLOG ##########\n");
		con_write(condev, buffer, strlen(buffer));
	}

	param = (uint_32 *) rebp();
	param += 3;

	for(pchar = format; *pchar != '\0'; pchar++) {
		
		if (*pchar == '%') {
			pchar++;
			switch (*pchar) {
				case 'd':  
					itoa(*param, buffer, 10);
					con_write(condev, buffer, strlen(buffer));
					break;
				case 'x':
					itoa(*param, buffer, 16);
					con_write(condev, buffer, strlen(buffer));
					break;
				case 's':
					con_write(condev,(void *) *param, strlen((char *) *param));
					break;
				case '%':
					buffer[0] = '%';
					con_write(condev, buffer, 1);
					param--;
					break;
			}
			param++;
		}
		else {
			con_write(condev, pchar, 1);
		}
	}
}



//LOG EN VGA
/*
void debug_log(char* format, ...) {

	uint_32 *param;
	char *pchar;
	char buffer[32]; 
	int col = 0;

	param = (uint_32 *) rebp();
	param += 3;

	if (debug_row == 0) {
		strcpy(buffer, "########## SYSLOG ##########\n");
		vga_write(debug_row, col, buffer, VGA_FC_WHITE);
		debug_row++;
	}

	for(pchar = format; *pchar != '\0'; pchar++) {
		
		if (*pchar == '%') {
			pchar++;
			switch (*pchar) {
				case 'd':  
					itoa(*param, buffer, 10);
					vga_write(debug_row, col, buffer, VGA_FC_WHITE);
					col += strlen(buffer);
					break;
				case 'x':
					itoa(*param, buffer, 16);
					vga_write(debug_row, col, buffer, VGA_FC_WHITE);
					col += strlen(buffer);
					break;
				case 'b':
					itoa(*param, buffer, 2);
					vga_write(debug_row, col, buffer, VGA_FC_WHITE);
					col += strlen(buffer);
					break;
				case 's':
					vga_write(debug_row, col,(char *) *param, VGA_FC_WHITE);
					col += strlen((char *) *param);
					break;
				case '%':
					vga_write(debug_row, col, "%s", VGA_FC_WHITE);
					col++;
					param--;
					break;
			}
			param++;
		}
		else {
			buffer[0] = *pchar;
			buffer[1] = '\0';
			vga_write(debug_row, col, buffer, VGA_FC_WHITE);
			col++;
		}
	}
	debug_row++;
}
*/

/*
//LOG EN SERIAL
void debug_log(char* format, ...) {

	uint_32 *param;
	char *pchar;
	char buffer[16]; 

	if (serialdev == NULL) {
		//puerto serie
		serialdev = serial_open(0);
	}

	param = (uint_32 *) rebp();
	param += 3;

	for(pchar = format; *pchar != '\0'; pchar++) {
		
		if (*pchar == '%') {
			pchar++;
			switch (*pchar) {
				case 'd':  
					itoa(*param, buffer, 10);
					serial_write(serialdev, buffer, strlen(buffer));
					break;
				case 'x':
					itoa(*param, buffer, 16);
					serial_write(serialdev, buffer, strlen(buffer));
					break;
				case 's':
					serial_write(serialdev,(void *) *param, strlen((char *) *param));
					break;
				case '%':
					buffer[0] = '%';
					serial_write(serialdev, &buffer[0], 1);
					param--;
					break;
			}
			param++;
			breakpoint();
		}
		else {
			serial_write(serialdev, pchar, 1);
		}
	}
}
*/


bool in_panic = FALSE;

void
debug_backtrace(uint_32* st) {
	int i = 0;
	uint_32* stk = st;
	vga_printf(USE_CURSOR, USE_CURSOR, "\nBacktrace: \n", COLOR);
	while (stk != NULL && (unsigned int) stk != 0xA0000)
	{
		//breakpoint();	
		vga_printf(USE_CURSOR, USE_CURSOR, "Frame %d EBP: %x ", COLOR, i, (unsigned int) stk);
		//breakpoint();	
		vga_printf(USE_CURSOR, USE_CURSOR, "Prev EBP: %x Prev: EIP %x Args: %x %x %x %x \n", COLOR,
			*stk, *(stk+1),
			*(stk+2), *(stk+3), *(stk+4), *(stk+5));
		i++;
		stk = (uint_32*) *stk;
	}
	vga_printf(USE_CURSOR, USE_CURSOR, "TOP OF STACK: %x \n", COLOR, (unsigned int) stk);	
}

void
debug_stack(const uint_32* stack) {
	int i = 0;
	vga_printf(USE_CURSOR, USE_CURSOR, "\nStack: \n", COLOR);
	for ( i = 0; i < 16; i+=4 )
	{
		vga_printf(USE_CURSOR, USE_CURSOR, "%x: %x %x %x %x \n", COLOR,
			((unsigned int) stack + i),
			stack[i+0], stack[i+1], stack[i+2], stack[i+3]);
	}
	vga_printf(USE_CURSOR, USE_CURSOR, "Done ", COLOR);	
}


void
debug_kernelpanic(const uint_32* stack, const exp_state* expst) {
	/* No permite panics anidados */
	
	if (in_panic) while(1) hlt();
	in_panic = TRUE;
	
	vga_clear();
	vga_printf(REGS_ROW + 0, REGS_COL, "EAX: %x", COLOR, expst->eax);
	vga_printf(REGS_ROW + 1, REGS_COL, "EBX: %x", COLOR, expst->ebx);
	vga_printf(REGS_ROW + 0, REGS_COL + 14, "ECX: %x", COLOR, expst->ecx);
	vga_printf(REGS_ROW + 1, REGS_COL + 14, "EDX: %x", COLOR, expst->edx);
	vga_printf(REGS_ROW + 0, REGS_COL + 28, "ESI: %x", COLOR, expst->esi);
	vga_printf(REGS_ROW + 1, REGS_COL + 28, "EDI: %x", COLOR, expst->edi);	
	vga_printf(REGS_ROW + 0, REGS_COL + 42, "EBP: %x", COLOR, expst->ebp);
	vga_printf(REGS_ROW + 1, REGS_COL + 42, "EFL: %x", COLOR, expst->eflags);
	vga_printf(REGS_ROW + 0, REGS_COL + 56, "ESP: %x:%x", COLOR, expst->ss, expst->esp);
	vga_printf(REGS_ROW + 1, REGS_COL + 56, "EIP: %x:%x", COLOR, expst->org_cs, expst->org_eip);	
	;	

	vga_printf(USE_CURSOR, USE_CURSOR, "\nException: %x ErrorCode: %x CR2: %x ", COLOR, expst->intcode, expst->errcode, rcr2());
	if ( expst->intcode < 18 )
		vga_printf(USE_CURSOR, USE_CURSOR, "MSG: %s", COLOR, exp_name[expst->intcode]);
	
	debug_backtrace((uint_32*)expst->ebp);
	debug_stack(stack);

	cli();
	hlt();
}

void
debug_init(void) {
	/* Registra todas las excepciones para sí */
	idt_register( 0x00 , debug_isr_00 , 0);
	idt_register( 0x01 , debug_isr_01 , 0);
	idt_register( 0x02 , debug_isr_02 , 0);
	idt_register( 0x03 , debug_isr_03 , 0);
	idt_register( 0x04 , debug_isr_04 , 0);
	idt_register( 0x05 , debug_isr_05 , 0);
	idt_register( 0x06 , debug_isr_06 , 0);
	idt_register( 0x07 , debug_isr_07 , 0);
	idt_register( 0x08 , debug_isr_08 , 0);
	idt_register( 0x09 , debug_isr_09 , 0);
	idt_register( 0x0A , debug_isr_0A , 0);
	idt_register( 0x0B , debug_isr_0B , 0);
	idt_register( 0x0C , debug_isr_0C , 0);
	idt_register( 0x0D , debug_isr_0D , 0);
	idt_register( 0x0E , debug_isr_0E , 0);
	idt_register( 0x10 , debug_isr_10 , 0);
	idt_register( 0x11 , debug_isr_11 , 0);
	idt_register( 0x12 , debug_isr_12 , 0);
	idt_register( 0x13 , debug_isr_13 , 0);
	idt_register( 0x20 , debug_isr_20 , 0);
	idt_register( 0x21 , debug_isr_21 , 0);
	idt_register( 0x22 , debug_isr_22 , 0);
	idt_register( 0x23 , debug_isr_23 , 0);
	idt_register( 0x24 , debug_isr_24 , 0);
	idt_register( 0x25 , debug_isr_25 , 0);
	idt_register( 0x26 , debug_isr_26 , 0);
	idt_register( 0x27 , debug_isr_27 , 0);
	idt_register( 0x28 , debug_isr_28 , 0);
	idt_register( 0x29 , debug_isr_29 , 0);
	idt_register( 0x2A , debug_isr_2A , 0);
	idt_register( 0x2B , debug_isr_2B , 0);
	idt_register( 0x2C , debug_isr_2C , 0);
	idt_register( 0x2D , debug_isr_2D , 0);
	idt_register( 0x2E , debug_isr_2E , 0);
	idt_register( 0x2F , debug_isr_2F , 0);
	idt_register( 0x30 , debug_isr_30 , 3);
}

